#pragma once

#include <condition_variable>


namespace evm {

using namespace std;

/* Semaphore that attempting to lock when `count_` is zero suspends
 * the calling thread until `count_` becomes nonzero again.
 *
 * It meets the BasicLockable requirements, and thus can be used such as:
 *
 * Semaphore semaphore(2);
 * lock_guard<Semaphore> lg(semaphore);
 */
struct Semaphore {
    Semaphore(size_t count = 1) : count_(count) {}

    /* Reset the counter. Note that it may lead to locking inconsistency. */
    void reset(size_t count) {
        count_ = count;
    }

    void lock() {
        unique_lock<mutex> lock(mutex_);
        while (!count_)  // if count_ == 0
            cv_.wait(lock);
        // Hereby count_ > 0
        --count_;
    }

    void unlock() {
        unique_lock<mutex> lock(mutex_);
        ++count_;
        cv_.notify_one();
    }

private:
    mutex mutex_;
    condition_variable cv_;
    size_t count_;
};

}
